package secondriver.process.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import secondriver.process.exception.ExternalFileLocationUnusableException;
import secondriver.process.exception.SettingException;
import spark.utils.StringUtils;


/**
 * Author : secondriver
 * Date :  2016/1/5
 */
public class Setting {


    private static final String DEFAULT_SETTING = "setting.properties";
    private static final String USER_HOME = System.getProperty("user.home");
    private static final String IMAGE_PROCESS = "image-process";
    private static final String DEFAULT_WWW = Paths.get(USER_HOME, IMAGE_PROCESS, "www").toAbsolutePath().toString();
    private static final String DEFAULT_CACHE = Paths.get(USER_HOME, IMAGE_PROCESS, "cache").toAbsolutePath().toString();

    private static final Logger LOGGER = LoggerFactory.getLogger(Setting.class);

    private int port = 4567;
    private int maxThreads = 10;
    private int minThreads = 5;
    private int threadIdleTimeoutMillis = 30000;

    private final String staticFileLocation = "public";
    private String externalStaticFileLocation = DEFAULT_WWW;
    private String cacheFileLocation = DEFAULT_CACHE;
    private int maxCache = 50;

    private String keystoreFile;
    private String keystorePassword;
    private String truststoreFile;
    private String truststorePassword;

    private boolean isSecureSupported = false;

    private EnhanceProperties config = new EnhanceProperties();

    private Setting() {
    }

    public static Setting create() throws SettingException {
        return create(null);
    }

    public static Setting create(String settingFile) throws SettingException {
        Setting settings = new Setting();

        InputStream in = null;
        boolean flag = false;
        if (StringUtils.isNotEmpty(settingFile)) {
            java.io.File config = new File(settingFile);
            if (config.exists() && config.isFile() && config.canRead()) {
                try {
                    in = new FileInputStream(config);
                    flag = true;
                } catch (FileNotFoundException e) {
                    LOGGER.warn("Setting file " + settingFile + " not found ", e);
                }
            }
        }
        if (!flag) {
            in = Setting.class.getClassLoader().getResourceAsStream(DEFAULT_SETTING);
        }
        try {
            if (in != null) {
                settings.config.load(in);
                if (flag) {
                    LOGGER.info("Setting file load from '" + settingFile + "'.");
                } else {
                    LOGGER.info("Setting file can't load from '" + settingFile + "' , and use default setting file '" + DEFAULT_SETTING + "'.");
                }
            } else {
                LOGGER.warn("Setting file load failed , and use default value.");
            }
        } catch (IOException e) {
            LOGGER.warn("Load setting file exception ", e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    LOGGER.warn("Close setting file inputStream exception ", e);
                }
            }
        }

        settings.parseSettingValue();
        return settings;
    }

    private void parseSettingValue() throws SettingException {

        try {
            port = Integer.parseInt(config.getProperty("port", String.valueOf(port)));

            maxThreads = Integer.parseInt(config.getProperty("maxThreads", String.valueOf(maxThreads)));
            minThreads = Integer.parseInt(config.getProperty("minThreads", String.valueOf(minThreads)));
            threadIdleTimeoutMillis = Integer.parseInt(config.getProperty("threadIdleTimeoutMillis", String.valueOf(threadIdleTimeoutMillis)));

            maxCache = Integer.parseInt(config.getProperty("maxCache", String.valueOf(maxCache)));
        } catch (NumberFormatException e) {
            throw new SettingException("NumberFormatException : " + e.getMessage());
        }

        cacheFileLocation = prepareFileDirectory("cacheFileLocation", cacheFileLocation,
                () -> "External Cache File Location '%d' create failed and please check it.");
        externalStaticFileLocation = prepareFileDirectory("externalStaticFileLocation", externalStaticFileLocation,
                () -> "External Static File Location '%d' create failed and please check it.");

        keystoreFile = config.getProperty("keystoreFile");
        keystorePassword = config.getProperty("keystorePassword");
        truststoreFile = config.getProperty("truststoreFile");
        truststorePassword = config.getProperty("truststorePassword");
        isSecureSupported = StringUtils.isNotEmpty(keystoreFile);
    }

    private String prepareFileDirectory(String key, String defaultValue, Supplier<String> supplier) throws ExternalFileLocationUnusableException {
        String path = config.getProperty(key, defaultValue);
        File file = new File(path);
        if (!file.exists() && !file.mkdirs()) {
            LOGGER.warn(String.format(supplier.get(), path));
        }
        if (file.exists() && file.isDirectory() && file.canRead()) {
            return file.getAbsolutePath();
        } else {
            throw new ExternalFileLocationUnusableException(file.getAbsolutePath());
        }
    }

    public int getPort() {
        return port;
    }

    public int getMaxThreads() {
        return maxThreads;
    }

    public int getMinThreads() {
        return minThreads;
    }

    public int getThreadIdleTimeoutMillis() {
        return threadIdleTimeoutMillis;
    }

    public String getStaticFileLocation() {
        return staticFileLocation;
    }

    public String getExternalStaticFileLocation() {
        return externalStaticFileLocation;
    }

    public String getTruststoreFile() {
        return truststoreFile;
    }

    public String getTruststorePassword() {
        return truststorePassword;
    }

    public boolean isSecureSupported() {
        return isSecureSupported;
    }

    public String getKeystorePassword() {
        return keystorePassword;
    }

    public String getKeystoreFile() {
        return keystoreFile;
    }

    public int getMaxCache() {
        return maxCache;
    }

    public String getCacheFileLocation() {
        return cacheFileLocation;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Setting{")
                .append("\n\tport='").append(port).append("'")
                .append("\n\tmaxThreads='").append(maxThreads).append("'")
                .append("\n\tminThreads='").append(minThreads).append("'")
                .append("\n\tthreadIdleTimeoutMillis='").append(threadIdleTimeoutMillis).append("'")
                .append("\n\tmaxCache='").append(maxCache).append("'")
                .append("\n\tcacheFileLocation='").append(cacheFileLocation).append("'")
                .append("\n\tstaticFileLocation='").append(staticFileLocation).append("'")
                .append("\n\texternalStaticFileLocation='").append(externalStaticFileLocation).append("'");

        if (isSecureSupported) {
            builder.append("\n\tisSecureSupported'").append(isSecureSupported).append("'")
                    .append("\n\tkeystoreFile='").append(keystoreFile).append("'")
                    .append("\n\tkeystorePassword='").append(keystorePassword).append("'")
                    .append("\n\ttruststoreFile='").append(truststoreFile).append("'")
                    .append("\n\ttruststorePassword='").append(truststorePassword).append("'");
        }
        builder.append("\n}");
        return builder.toString();
    }
}